
package com.x52im.rbchat.network.http.bigfile;

import android.content.Context;
import android.util.Log;

import com.eva.android.HttpBigFileDownloadHelper;
import com.x52im.rbchat.logic.chat_root.sendfile.ReceivedFileHelper;

/**
 * 大文件后台下载任管理类（基于性能、体验等考虑，当前大文件下载跟微信一样：一次只允许同时下载一个文件！）。
 *
 * @author Jack Jiang
 * @since 4.3
 */
public class BigFileDownloadManager
{
    private final static String TAG = BigFileDownloadManager.class.getSimpleName();

    private Context context = null;
    private BigFileDownloadObserver downloadObserver = null;

    private String fileName;
    /** 文件存储的目录：此目录末尾不需带"/"反斜线 */
    private String fileDir;
    private String fileMd5;
    private long fileLength;

    private FileStatus fileStatus = null;

    /** 下载任务异步执行AsyncTask */
    private HttpBigFileDownloadHelper.DownloadAsyncRoot downloadTask = null;

    public BigFileDownloadManager(Context context)
    {
        this.context = context;
    }

    /**
     重置本下载管理器中的参数为初始状态。
     本方法的调用主要用于APP中切换账号时，防止数据污染。
     */
    public void clear()
    {
        this.downloadTask = null;

        this.fileName = null;
        this.fileDir = null;
        this.fileMd5 = null;
        this.fileLength = 0;

        this.fileStatus = null;
    }

    /**
     * 开始/继续下载。
     *
     * @param _fileMd5
     * @param _currentLength
     * @param _fileDir
     * @param _fileName
     * @param _fileLength
     */
    public void startTask(String _fileMd5, long _currentLength
            , String _fileDir, String _fileName, long _fileLength)
    {
        // 如果任务已经存在则无条件先退出前面的任务（只允许同时进行一个任务的下载）
        // * 注意：此行应放置本方法中的最前面，不然刚设置好this.fileXXXX这些后，
        // * cancelTask就把变量置空，就会导致此次下载无交参数的出现
        this.cancelTask(true);

        // 本次下载的基本文件信息
        this.fileMd5 = _fileMd5;
        this.fileDir = _fileDir;
        this.fileName = _fileName;
        this.fileLength = _fileLength;

        // 下载链接
        String downURL = ReceivedFileHelper.getBigFileDownloadURL(context, fileMd5, _currentLength);

        Log.d(TAG, "[大文件下载管理器]马上开始从" + downURL + "下载文件数据了。。。。。。。。");
        printDebug();

        // 新建下载任务
        this.downloadTask = new HttpBigFileDownloadHelper.DownloadAsyncRoot(context, downURL, fileDir, this.fileName, fileLength)
        {
            private int oldProgress = -1;

            @Override
            protected void onPreExecute()
            {
                Log.d(TAG, "[大文件下载管理器] onPreExecute -------------");
                printDebug();

                oldProgress = -1;
                fileStatus = FileStatus.FILE_DOWNLOADING;

                if (downloadObserver != null)
                    downloadObserver.onPreExecute(fileMd5);
            }

            @Override
            protected void onProgressUpdate(Integer... progress)
            {
                // 确保只有进度值发生变化时才通知观察者（刷新UI），不然在大文件下载时因每次下载的块较大，
                // 转成100以内的整数进度值时，这个值很久才会变成下一个进度，导致频繁无节致地通知观察者，
                // 从而导致UI刷新太频繁，使得APP用起来变的非常卡！
                if (progress[0] != oldProgress)
                {
                    Log.d(TAG, "[大文件下载管理器] onProgressUpdate(" + progress[0] + ") -------------");

                    // 按照现在的逻辑，基本上0~100的值变化，再大的文件也就是会通知100次左右，app也不会卡了
                    if (downloadObserver != null)
                        downloadObserver.onProgressUpdate(fileMd5, progress);

                    // 保存上一个进度值
                    oldProgress = progress[0];
                }
            }

            @Override
            protected void onPostExecute_onException(Exception exception)
            {
                Log.e(TAG, "[大文件下载管理器] onPostExecute_onException -------------", exception);
                printDebug();

                // 异步发生时，就自动调用暂停逻辑
                pauseTask();

                if (downloadObserver != null)
                    downloadObserver.onPostExecute_onException(fileMd5, exception);
            }

            @Override
            protected void onPostExecute_onSucess(String fileSavedPath)
            {
                Log.i(TAG, "[大文件下载管理器] onPostExecute_onSucess(" + fileSavedPath + ") -------------");
                printDebug();

                fileStatus = FileStatus.FILE_COMPLETE;

                if (downloadObserver != null)
                    downloadObserver.onPostExecute_onSucess(fileMd5, fileSavedPath);
            }
        };

        // 实际上文件已经下载完成，不需要真正的进行网络下载了，直接走任务完成逻辑即可
        if(_currentLength == _fileLength)
        {
            Log.i(TAG, "[大文件下载管理器] 文件"+_fileName+"已经下载完成了，本次任务不需要真的从网络下载！(_currentLength="
                    +_currentLength+", _fileLength="+_fileLength+")");
            downloadTask.forceComplete();
        }
        else
        {
            // 执行下载任务
            downloadTask.execute();
        }
    }

    /**
     * 取消本次下载任务。
     *
     * @param notificationObserver
     */
    public void cancelTask(boolean notificationObserver)
    {
        Log.d(TAG, "[大文件下载管理器] cancelTask()已被调用。。");
        printDebug();

        if (this.downloadTask != null)// && !downloadTask.isComplete() && !downloadTask.isCancelled())
        {
            try
            {

                // 注意：AsyncTask cancel时，是肯定不会走onPostExecute方法的
                this.downloadTask.cancel(true);

//                this.downloadTask = null;
//
//                this.fileName = null;
//                this.fileDir = null;
//                this.fileMd5 = null;
//                this.fileLength = 0;

                // 重置
                this.clear();

                Log.i(TAG, "[大文件下载管理器] cancelTask()成功【OK】");

            }
            catch(Exception e)
            {
                Log.w(TAG, "[大文件下载管理器] cancelTask()时发生了异常【NO】", e);
            }
        }

        this.fileStatus = FileStatus.FILE_NOT_COMPLETE;

        if (notificationObserver && downloadObserver != null)
            downloadObserver.onCancel(fileMd5);
    }

    /**
     * 暂时本次下载（与取消下载相比，唯一的区别是设置fileStatus状态的不同，进而界面UI上的显示会有不同，仅此而已）
     */
    public void pauseTask()
    {
        Log.d(TAG, "[大文件下载管理器] pauseTask()已被调用。。");
        printDebug();

        if (downloadTask != null)// && !downloadTask.isComplete() && !downloadTask.isCancelled())
        {
            try
            {
                // 注意：AsyncTask时，是肯定不会走onPostExecute方法的
                downloadTask.cancel(true);
                downloadTask = null;

                Log.i(TAG, "[大文件下载管理器] pauseTask()成功【OK】");
            }
            catch(Exception e)
            {
                Log.w(TAG, "[大文件下载管理器] pauseTask()时发生了异常【NO】", e);
            }
        }

        fileStatus = FileStatus.FILE_DOWNLOAD_PAUSE;

        if (downloadObserver != null)
            downloadObserver.onPause(fileMd5);
    }

    /**
     * 是否有正在下载中的任务。
     *
     * @return true表示有任务（下载中），否则表示无有效任务（已下载完成或未有过下载任务）
     */
    public boolean isDownloading()
    {
        return this.fileStatus == FileStatus.FILE_DOWNLOADING;
    }

    /**
     * 是否有暂停中的任务。
     *
     * @return
     */
    public boolean isPause()
    {
        return this.fileStatus == FileStatus.FILE_DOWNLOAD_PAUSE;
    }

    public FileStatus getFileStatus()
    {
        return this.fileStatus;
    }

    /**
     * 当前任务中的文件是否是指定文件。
     *
     * @param fileMd5 文件的md5码
     * @return true表示是，否则不是
     */
    public boolean isCurrentFile(String fileMd5)
    {
        if (this.fileMd5 != null
                && fileMd5 != null
                && this.fileMd5.toLowerCase().equals(fileMd5.toLowerCase())) {
            return true;
        }

        return false;
    }

    public void setDownloadObserver(BigFileDownloadObserver downloadObserver)
    {
        this.downloadObserver = downloadObserver;
    }

    public String getFileName()
    {
        return this.fileName;
    }

    public void printDebug()
    {
        Log.w(TAG, "[大文件下载管理器] [fileStatus=" + this.fileStatus + ", isDownloading?" + this.isDownloading()
                + "]fileName=" + fileName + ", fileDir=" + fileDir
                + ", fileMd5=" + fileMd5 + ", fileLen=" + fileLength);
    }


    /**
     * 文件任务下载状态观察者。
     */
    public static abstract class BigFileDownloadObserver
    {
        protected abstract void onPreExecute(String fileMd5InManager);

        protected abstract void onProgressUpdate(String fileMd5InManager, Integer... progress);

        protected abstract void onPostExecute_onException(String fileMd5InManager, Exception exception);

        protected abstract void onPostExecute_onSucess(String fileMd5InManager, String fileSavedPath);

        protected abstract void onCancel(String fileMd5InManager);

        protected abstract void onPause(String fileMd5InManager);
    }

    /**
     * 当前文件状态。
     */
    public enum FileStatus
    {
        /** 文件已存在，无需下载 */
        FILE_COMPLETE,
        /** 文件正在下载中 */
        FILE_DOWNLOADING,
        /**　文件下载已暂停 */
        FILE_DOWNLOAD_PAUSE,
        /** 文件处于未下载完成状态 */
        FILE_NOT_COMPLETE
    }
}